<script setup lang="ts">
import { ref, reactive, computed, onMounted, nextTick } from 'vue'
import { ElInput, ElButton, ElScrollbar, ElAvatar, ElDivider, ElMessage } from 'element-plus'
import { sendMessageToDify, DifyRequest, DifyConfig, DifyStreamingResponse } from '@/api/dify'
import MarkdownIt from 'markdown-it'
import * as echarts from 'echarts'
import { DIFY_API_BASE_URL } from '@/constants/dify'

// 创建markdown-it实例
const md = new MarkdownIt({
  html: true, // 禁用HTML标签
  breaks: true, // 转换'\n'为<br>
  linkify: true, // 自动识别URL并转换为链接
  typographer: true, // 启用一些语言中性的替换和引号美化
  highlight: function (code, lang) {
    return `<pre class="language-${lang}"><code>${code}</code></pre>`
  }
})

// 接收属性
const props = defineProps({
  apiKey: {
    type: String,
    default: 'app-XBcoWGd3UPMrLaJMljSdMGdc'
  },
  apiBase: {
    type: String,
    default: DIFY_API_BASE_URL
  },
  placeholder: {
    type: String,
    default: '请输入您的问题...'
  },
  inputs: {
    type: Object,
    default: () => ({})
  },
  welcomeMessage: {
    type: String,
    default: '你好！我是电费异常识别系统的智能助手，有什么可以帮助您的？'
  }
})

// 聊天消息类型
interface ChatMessage {
  id: string
  content: string
  role: 'user' | 'assistant'
  loading?: boolean
  createdAt: Date
  lastText?: string
  charts?: { [key: string]: any }
}

// 聊天状态
const chatState = reactive({
  messages: [] as ChatMessage[],
  currentInput: '',
  loading: false,
  conversationId: '',
  // 跟踪用户是否已阅读到底部
  userScrollPosition: {
    scrollTop: 0,
    scrollHeight: 0,
    clientHeight: 0
  }
})

// 滚动区域引用
const chatScrollRef = ref<InstanceType<typeof ElScrollbar> | null>(null)

// 消息输入框引用
const inputRef = ref<InstanceType<typeof ElInput> | null>(null)

// 是否允许自动滚动到底部
const shouldAutoScroll = ref(true)

// 发送按钮状态
const canSend = computed(() => !chatState.loading && chatState.currentInput.trim() !== '')

// 定义要向父组件传递的事件
const emit = defineEmits([
  'message-end',
  'json-extracted',
  'onFinish',
  'closeElDrawer',
  'details-extracted',
  'getTaskID'
])

// 检查是否在底部附近
const isNearBottom = (
  scrollTop: number,
  scrollHeight: number,
  clientHeight: number,
  threshold = 50
) => {
  return scrollHeight - scrollTop - clientHeight < threshold
}

// 自动滚动到底部
const scrollToBottom = () => {
  setTimeout(() => {
    try {
      const container = document.querySelector('.simple-chat .el-scrollbar__wrap')
      if (container && shouldAutoScroll.value) {
        container.scrollTop = container.scrollHeight
      }
    } catch (e) {
      console.error('滚动失败:', e)
    }
  }, 50)
}

// 添加消息
const addMessage = (content: string, role: 'user' | 'assistant', loading = false) => {
  const message = {
    id: Date.now().toString() + Math.random().toString(36).substring(2, 9),
    content,
    role,
    loading,
    createdAt: new Date(),
    lastText: '',
    charts: {}
  }
  chatState.messages.push(message)

  // 添加用户消息时，始终滚动到底部
  if (role === 'user') {
    shouldAutoScroll.value = true
    scrollToBottom()
  } else {
    // 助手消息，检查用户是否在底部附近
    const { scrollTop, scrollHeight, clientHeight } = chatState.userScrollPosition
    shouldAutoScroll.value = isNearBottom(scrollTop, scrollHeight, clientHeight)
    scrollToBottom()
  }

  return message
}

// 渲染图表
const renderCharts = (messageId: string) => {
  nextTick(() => {
    const message = chatState.messages.find((m) => m.id === messageId)
    if (!message || !message.charts) return

    Object.keys(message.charts).forEach((chartId) => {
      const chartData = message.charts![chartId]
      const chartDom = document.getElementById(chartId)
      if (chartDom) {
        try {
          const chart = echarts.init(chartDom)
          chart.setOption(chartData)
        } catch (error) {
          console.error('图表渲染失败:', error)
        }
      }
    })
  })
}

// 从内容中提取echarts配置
const extractEchartsConfig = (content: string, messageId: string) => {
  const echartsRegex = /```echarts\s*([\s\S]*?)\s*```/g
  const echartsMatches = [...content.matchAll(echartsRegex)]
  const charts: { [key: string]: any } = {}

  if (echartsMatches.length > 0) {
    echartsMatches.forEach((match, index) => {
      try {
        const chartConfig = JSON.parse(match[1].trim())
        const chartId = `chart-${messageId}-${index}`
        charts[chartId] = chartConfig

        // 替换Markdown中的echarts代码块为div容器
        content = content.replace(
          match[0],
          `<div id="${chartId}" class="echarts-container" style="width: 100%; height: 350px;"></div>`
        )
      } catch (error) {
        console.error('解析echarts配置失败:', error)
      }
    })
  }

  return { content, charts }
}

// 格式化消息内容（支持Markdown和高亮代码以及echarts图表）
const formatMessage = (message: ChatMessage) => {
  if (!message || !message.content) return ''

  let content = message.content
  console.log('message==========》', content)

  // 先移除code-block包装，因为markdown-it会处理```代码块
  content = content.replace(
    /<div class="code-block"><pre>([\s\S]*?)<\/pre><\/div>/g,
    '```\n$1\n```'
  )

  // 移除<think>标签内容
  content = content.replace(/<think>([\s\S]*?)<\/think>/g, '')

  // 移除<details>标签及其内容
  // content = content.replace(/<details>[\s\S]*?<\/details>/g, '')

  // 移除JSON代码块，避免显示在界面上
  // content = content.replace(/```json\s*([\s\S]*?)\s*```/g, '')

  // 处理/files/tools路径，在前面添加域名前缀
  content = content.replace(/\/files\/tools/g, `${DIFY_API_BASE_URL}/files/tools`)

  // 提取echarts配置
  const { content: processedContent, charts } = extractEchartsConfig(content, message.id)

  // 如果有图表配置，保存到消息对象中
  if (Object.keys(charts).length > 0) {
    message.charts = { ...message.charts, ...charts }
    // 延迟渲染图表，确保DOM已更新
    setTimeout(() => renderCharts(message.id), 100)
  }

  try {
    // 使用markdown-it渲染
    return md.render(processedContent)
  } catch (err) {
    console.error('Markdown渲染错误:', err)

    // 如果markdown渲染失败，回退到原始处理方式
    // 替换换行符为<br>
    return processedContent.replace(/\n/g, '<br />')
  }
}

// 处理滚动事件
const handleScroll = (args: { scrollTop: number; scrollLeft: number }) => {
  const container = document.querySelector('.simple-chat .el-scrollbar__wrap')
  if (container) {
    const { scrollTop } = args
    const { scrollHeight, clientHeight } = container

    // 更新用户的滚动位置
    chatState.userScrollPosition = {
      scrollTop,
      scrollHeight,
      clientHeight
    }

    shouldAutoScroll.value = isNearBottom(scrollTop, scrollHeight, clientHeight)
  }
}

// 发送消息
const sendMessage = async () => {
  if (!chatState.currentInput.trim() || chatState.loading) return

  const userMessage = chatState.currentInput.trim()
  addMessage(userMessage, 'user')

  // 清空输入框并聚焦
  chatState.currentInput = ''
  setTimeout(() => inputRef.value?.focus(), 100)

  // 创建助手消息
  const assistantMessage = addMessage('', 'assistant', true)
  assistantMessage.lastText = ''

  chatState.loading = true

  try {
    const config: DifyConfig = {
      apiKey: props.apiKey,
      apiBase: props.apiBase
    }

    // 确保inputs是有效的对象
    const inputs = typeof props.inputs === 'object' && props.inputs !== null ? props.inputs : {}

    const request: DifyRequest = {
      query: userMessage,
      response_mode: 'streaming',
      user: 'user-20250515',
      inputs: inputs
    }

    if (chatState.conversationId) {
      request.conversation_id = chatState.conversationId
    }

    // 请求日志，便于调试
    console.log(
      '发送请求到Dify:',
      JSON.stringify({
        query: request.query,
        inputs_summary: Object.keys(inputs || {}),
        conversation_id: request.conversation_id
      })
    )

    await sendMessageToDify(
      request,
      config,
      (text, taskID) => {
        assistantMessage.loading = false
        chatState.loading = false
        emit('getTaskID', taskID)
        // 直接更新消息内容，确保实时显示
        assistantMessage.content += text
        assistantMessage.lastText = text
        // 强制更新视图
        chatState.messages = [...chatState.messages]

        // 检查是否需要滚动到底部（只有当用户当前视图接近底部时才滚动）
        const { scrollTop, scrollHeight, clientHeight } = chatState.userScrollPosition
        shouldAutoScroll.value = isNearBottom(scrollTop, scrollHeight, clientHeight)
        scrollToBottom()
      },
      (response) => {
        if (response && response.conversation_id && !chatState.conversationId) {
          chatState.conversationId = response.conversation_id
        }
        console.log('response==========》', response)

        // 处理消息内容，移除<think>标签内容并提取JSON
        const processedContent = extractContentAndJson(assistantMessage.content)
        assistantMessage.content = processedContent.content

        // 如果有JSON数据，向父组件发送事件
        if (processedContent.jsonData) {
          console.log('提取到的JSON数据:', processedContent.jsonData)
          emit('json-extracted', processedContent.jsonData)
        }

        // 如果有details内容，向父组件发送事件
        if (processedContent.content) {
          console.log('提取到的content内容:', processedContent.content)
          emit('details-extracted', processedContent.content)
        }

        assistantMessage.loading = false
        chatState.loading = false

        // 向父组件发送会话结束事件和完整响应
        const result = {
          message: assistantMessage.content,
          response: response,
          extractedJson: processedContent.jsonData,
          detailsContent: processedContent.content
        }

        emit('message-end', result)

        // 同时触发 onFinish 事件以兼容之前的代码
        console.log('回答完成')
        emit('onFinish', result)
      },
      (error) => {
        assistantMessage.content = '抱歉，消息发送失败，请重试。' + (error.message || '')
        assistantMessage.loading = false
        chatState.loading = false
      }
    )
  } catch (error) {
    assistantMessage.content = '抱歉，消息发送失败，请重试。'
    assistantMessage.loading = false
    chatState.loading = false
  }
}

// 处理提取的JSON数据
const extractContentAndJson = (text: string) => {
  const result = {
    content: text,
    jsonData: null as any
  }

  console.log('开始处理文本内容:', text)

  // 移除<think>标签内容
  result.content = text.replace(/<think>([\s\S]*?)<\/think>/g, '')

  // 尝试多种JSON提取模式
  const jsonPatterns = [
    // 模式1: ```json ... ``` 格式
    /```json\s*([\s\S]*?)\s*```/g,
    // 模式2: ``` ... ``` 格式（不带json标记）
    /```\s*([\s\S]*?)\s*```/g,
    // 模式3: 直接是JSON数组格式
    /\[\s*\{[\s\S]*?\}\s*\]/g
  ]

  let jsonArray: any[] = []
  let hasValidJson = false

  // 尝试每种模式
  for (const pattern of jsonPatterns) {
    const matches = [...result.content.matchAll(pattern)]
    if (matches.length > 0) {
      console.log(`使用模式 ${pattern} 找到 ${matches.length} 个匹配项`)

      for (const match of matches) {
        try {
          // 获取JSON文本并清理
          let jsonText = match[1] || match[0]
          jsonText = jsonText.trim()

          // 尝试解析JSON
          const parsed = JSON.parse(jsonText)

          // 处理解析结果
          if (Array.isArray(parsed)) {
            // 如果是数组，直接添加所有元素
            jsonArray = jsonArray.concat(parsed)
            console.log('成功解析JSON数组，长度:', parsed.length)
            hasValidJson = true
          } else if (typeof parsed === 'object') {
            // 如果是单个对象，添加到数组
            jsonArray.push(parsed)
            console.log('成功解析JSON对象')
            hasValidJson = true
          }
        } catch (err) {
          console.warn('JSON解析失败:', err, '原始文本:', jsonText)
          // 尝试修复常见的JSON格式问题
          try {
            // 1. 处理可能的单引号问题
            const fixedText = jsonText.replace(/'/g, '"')
            const parsed = JSON.parse(fixedText)
            if (Array.isArray(parsed)) {
              jsonArray = jsonArray.concat(parsed)
              hasValidJson = true
            } else {
              jsonArray.push(parsed)
              hasValidJson = true
            }
            console.log('修复单引号后成功解析JSON')
          } catch (fixErr) {
            console.warn('修复后仍然无法解析JSON:', fixErr)
          }
        }
      }

      // 如果找到有效的JSON，跳出循环
      if (hasValidJson) {
        break
      }
    }
  }

  // 设置结果
  try {
    // 确保所有对象都有必要的字段
    result.jsonData = jsonArray
    console.log('最终处理后的JSON数据:', result.jsonData)
  } catch (error) {
    throw new Error(`JSON数据格式验证失败: ${error.message}`)
  }

  // 清理内容中的多余空行
  result.content = result.content.replace(/\n{3,}/g, '\n\n').trim()

  return result
}

// 监听键盘事件
const handleKeyDown = (e: KeyboardEvent) => {
  if (e.key === 'Enter' && !e.shiftKey) {
    e.preventDefault()
    sendMessage()
  }
}

// 清空会话-重置
const clearConversation = (val?: string) => {
  chatState.messages = []
  chatState.conversationId = ''

  // 添加欢迎消息
  addMessage(props.welcomeMessage, 'assistant')
  if (val && val == '01') return
  ElMessage.success('对话已清空')
}
// 关闭弹出抽屉
const closeElDrawer = () => {
  emit('closeElDrawer')
}

// 暴露方法给父组件
defineExpose({
  addMessage,
  clearConversation
})

// 历史记录
// 初始化
onMounted(() => {
  // 添加欢迎消息
  addMessage(props.welcomeMessage, 'assistant')

  // 设置输入框焦点
  if (inputRef.value) {
    inputRef.value.focus()
  }

  // 初始滚动到底部
  shouldAutoScroll.value = true
  scrollToBottom()

  // 初始化滚动位置信息
  setTimeout(() => {
    const container = document.querySelector('.simple-chat .el-scrollbar__wrap')
    if (container) {
      const { scrollTop, scrollHeight, clientHeight } = container
      chatState.userScrollPosition = {
        scrollTop,
        scrollHeight,
        clientHeight
      }
    }
  }, 100)
})
</script>

<template>
  <div class="simple-chat">
    <!-- 聊天区域 -->
    <div class="chat-container">
      <ElScrollbar ref="chatScrollRef" class="chat-messages" @scroll="handleScroll">
        <div class="message-list">
          <div
            v-for="message in chatState.messages"
            :key="message.id"
            :class="['message', `message-${message.role}`]"
          >
            <div class="message-avatar">
              <ElAvatar :size="40" v-if="message.role === 'assistant'">
                <img src="@/assets/images/robot-icon.jpg" />
              </ElAvatar>
              <ElAvatar :size="40" v-else src="">
                <img src="@/assets/images/userIcon.png" />
              </ElAvatar>
            </div>
            <div class="message-content">
              <div class="message-text markdown-body" v-html="formatMessage(message)"></div>
              <div v-if="message.loading" class="message-loading">
                <span class="dot-1"></span>
                <span class="dot-2"></span>
                <span class="dot-3"></span>
              </div>
            </div>
          </div>
        </div>
      </ElScrollbar>
    </div>

    <!-- 输入区域 -->
    <div class="chat-input-container">
      <div class="chat-toolbar">
        <div class="chat-toolbar-left">
          <div class="chat-left-btn" style="display: none">
            <img class="chat-left-img" src="@/assets/images/history-icon.png" alt="历史记录" />
            <span style="color: #98999a; font-size: 14px; padding-left: 4px">历史记录</span>
          </div>
          <div class="chat-left-btn" @click="clearConversation">
            <img class="chat-left-img" src="@/assets/images/clear-icon.png" alt="重置" />
            <span style="color: #98999a; font-size: 14px; padding-left: 4px">重置</span>
          </div>
        </div>
        <div class="chat-toolbar-right">
          <img
            class="chat-right-img"
            src="@/assets/images/retract-icon.png"
            alt="收回"
            @click="closeElDrawer"
          />
        </div>
      </div>

      <div class="chat-input">
        <ElInput
          ref="inputRef"
          v-model="chatState.currentInput"
          :placeholder="placeholder"
          type="textarea"
          :rows="3"
          resize="none"
          @keydown="handleKeyDown"
        />
        <div :disabled="!canSend" @click="sendMessage" class="send-button">
          <img src="@/assets/images/u2805.svg" alt="发送" />
        </div>
      </div>
    </div>
  </div>
</template>

<style lang="scss" scoped>
.simple-chat {
  display: flex;
  flex-direction: column;
  height: 100%;
  background-color: #ecf4fc;
  border-radius: 8px;
  overflow: hidden;
}

.chat-container {
  flex: 1;
  overflow: hidden;
  padding: 16px;
}

.chat-messages {
  height: 100%;
}

.message-list {
  padding: 10px 0;
}

.message {
  display: flex;
  margin-bottom: 20px;

  &.message-user {
    flex-direction: row-reverse;

    .message-content {
      background-color: #e6f6ff;
      border-radius: 10px 0 10px 10px;
      margin-right: 10px;
    }
  }

  &.message-assistant {
    .message-content {
      // background-color: #fff;
      background-color: #e6f6ff;
      border-radius: 0 10px 10px 10px;
      margin-left: 10px;
      // border: 1px solid #eaeaea;
    }
  }
}

.message-avatar {
  flex-shrink: 0;
}

.message-content {
  padding: 12px 16px;
  max-width: 80%;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);

  :deep(.markdown-body) {
    background-color: transparent;
    line-height: 1.6;
    word-break: break-word;

    /* Markdown 样式 */
    h1,
    h2,
    h3,
    h4,
    h5,
    h6 {
      margin-top: 24px;
      margin-bottom: 16px;
      font-weight: 600;
      line-height: 1.25;
    }

    h1 {
      font-size: 2em;
      margin-top: 0;
    }

    h2 {
      font-size: 1.5em;
    }

    h3 {
      font-size: 1.25em;
    }

    h4 {
      font-size: 1em;
    }

    p {
      margin-top: 0;
      margin-bottom: 10px;
    }

    a {
      color: #0366d6;
      text-decoration: none;

      &:hover {
        text-decoration: underline;
      }
    }

    code {
      font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
      background-color: rgba(27, 31, 35, 0.05);
      padding: 0.2em 0.4em;
      border-radius: 3px;
      font-size: 85%;
    }

    pre {
      background-color: #1e1e1e;
      border-radius: 6px;
      padding: 16px;
      overflow: auto;
      margin: 16px 0;

      code {
        background-color: transparent;
        padding: 0;
        white-space: pre;
        font-size: 85%;
        color: #e0e0e0;
      }
    }

    blockquote {
      padding: 0 1em;
      color: #6a737d;
      border-left: 0.25em solid #dfe2e5;
      margin: 0 0 16px 0;
    }

    ul,
    ol {
      padding-left: 2em;
      text-align: start;
      margin-top: 0;
      margin-bottom: 16px;
    }

    table {
      display: block;
      width: 100%;
      overflow: auto;
      margin-top: 0;
      margin-bottom: 16px;
      border-collapse: collapse;

      th,
      td {
        padding: 6px 13px;
        border: 1px solid #dfe2e5;
      }

      th {
        font-weight: 600;
        background-color: #f6f8fa;
      }

      tr:nth-child(2n) {
        background-color: #f6f8fa;
      }
    }

    img {
      max-width: 100%;
      box-sizing: content-box;
    }

    hr {
      height: 0.25em;
      padding: 0;
      margin: 24px 0;
      background-color: #e1e4e8;
      border: 0;
    }

    .echarts-container {
      margin: 16px 0;
      border: 1px solid #eaeaea;
      border-radius: 6px;
    }
  }

  :deep(.code-block) {
    margin: 12px 0;
    background-color: #1e1e1e;
    border-radius: 6px;
    overflow: hidden;

    pre {
      padding: 12px;
      margin: 0;
      font-family: 'Courier New', Courier, monospace;
      white-space: pre-wrap;
      color: #e0e0e0;
    }
  }
}

.message-text {
  word-break: break-word;
  // white-space: pre-wrap;
  line-height: 1.6;
}

.message-loading {
  margin-top: 8px;
  display: flex;

  .dot-1,
  .dot-2,
  .dot-3 {
    width: 6px;
    height: 6px;
    background: #ccc;
    border-radius: 50%;
    margin-right: 5px;
    animation: dot-bounce 1.4s infinite ease-in-out both;
  }

  .dot-1 {
    animation-delay: -0.32s;
  }

  .dot-2 {
    animation-delay: -0.16s;
  }
}

@keyframes dot-bounce {
  0%,
  80%,
  100% {
    transform: scale(0);
  }
  40% {
    transform: scale(1);
  }
}

.chat-input-container {
  padding: 0 16px 16px;
}

.chat-toolbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-bottom: 12px;
  border-bottom: 1px solid #dcdfe6;
  margin-bottom: 16px;
}

.chat-toolbar-left {
  display: flex;
}

.chat-left-btn {
  display: flex;
  background-color: rgba(255, 255, 255, 0.6);
  box-sizing: border-box;
  border-width: 1px;
  border-style: solid;
  border-color: rgba(231, 236, 244, 1);
  border-radius: 4px;
  padding: 4px 6px;
  margin-right: 4px;
}

.chat-left-img {
  width: 20px;
  height: 20px;
}

.chat-right-img {
  width: 30px;
  height: 30px;
}

.chat-input {
  display: flex;
  // align-items: flex-end;
  align-items: center;

  :deep(.el-textarea) {
    flex: 1;
    margin-right: 10px;
  }

  .send-button {
    height: 40px;
    width: 40px;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
  }
}
</style>
